package gredis

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis/v8"
	"net"
	"sanrenx.com/xyxf/pkg/setting"
	"time"
)

//RedisClient 连接客户端
var RedisClient *redis.Client

func Setup() {
	RedisClient = redis.NewClient(&redis.Options{
		//连接信息
		Network:  "tcp",                         //网络类型, tcp 或者 unix, 默认tcp
		Addr:     setting.RedisSetting.Host,     //ip:port
		Username: setting.RedisSetting.Username, //用户名, 使用指定用户名验证当前连接
		Password: setting.RedisSetting.Password, //密码,
		DB:       setting.RedisSetting.DB,       //连接后选中的redis数据库index

		//命令执行失败时的重试策略
		MaxRetries:      3,                      //命令执行失败时最大重试次数，默认3次重试。
		MinRetryBackoff: 8 * time.Millisecond,   //每次重试最小间隔时间，默认8ms，-1表示取消间隔
		MaxRetryBackoff: 512 * time.Millisecond, //每次重试最大时间间隔，默认512ms，-1表示取消间隔

		//超时
		DialTimeout:  5 * time.Second, //连接建立超时时间，默认5秒
		ReadTimeout:  3 * time.Second, //读超时，默认3秒，-1表示取消读超时
		WriteTimeout: 3 * time.Second, //写超时，默认与读超时相等

		//连接池容量、闲置连接数量、闲置连接检查
		PoolSize:           16,                               //连接池最大Socket连接数，默认为10倍CPU数量，10 * runtime.NumCPU()
		MinIdleConns:       8,                                //启动阶段创建指定数量的Idle连接，并长期维持Idle状态的连接数不少于指定数量。
		MaxConnAge:         0 * time.Second,                  //连接存活时长，超过指定时长则关闭连接。默认为0，不关闭旧连接。
		PoolTimeout:        4 * time.Second,                  //当所有连接都处于繁忙状态时，客户端等待可用连接的最大等待时长。默认为读超时+1秒
		IdleTimeout:        setting.RedisSetting.IdleTimeout, //关闭闲置连接时间，默认5分钟，-1表示取消闲置超时检查
		IdleCheckFrequency: 1 * time.Minute,                  //闲置连接检查周期，默认为1分钟；-1表示不做检查，只在客户端获取连接时对闲置连接进行处理。

		//自定义连接函数
		Dialer: func(ctx context.Context,
			network string,
			addr string,
		) (net.Conn, error) {
			netDialer := net.Dialer{
				Timeout:   5 * time.Second,
				KeepAlive: 5 * time.Minute,
			}
			return netDialer.Dial(network, addr)
		},

		//钩子函数，建立新连接时调用
		OnConnect: func(ctx context.Context,
			cn *redis.Conn,
		) error {
			fmt.Println("Redis Conn =", cn)
			return nil
		},
	})

	ctx := context.Background()
	pong, err := RedisClient.Ping(ctx).Result()
	fmt.Println("Redis Ping =", pong, err)
}

//Exist 判断key是否在Redis中
func Exist(
	ctx context.Context,
	key string,
) bool {
	value, err := RedisClient.Exists(ctx, key).Result()
	if err != nil {
		return false
	}
	return value > 0
}

//Delete 从Redis中删除键值对
func Delete(
	ctx context.Context,
	key string,
) (bool, error) {
	value, err := RedisClient.Del(ctx, key).Result()
	return value > 0, err
}

//Delete 设置Key的过期时间
func PExpire(
	ctx context.Context,
	key string,
	t time.Duration,
) (bool, error) {
	return RedisClient.PExpire(ctx, key, t).Result()
}

//String 相关操作

//Set 操作String类型, 将数据写入Redis
//	t 过期时间, 单位是秒, 0表示没有过期时间
func Set(
	ctx context.Context,
	key string,
	data interface{},
	t time.Duration,
) error {
	value, err := json.Marshal(data)
	if err != nil {
		return err
	}
	t = t * time.Second
	return RedisClient.Set(ctx, key, value, t).Err()
}

//GetArticleById 操作String类型, 从Redis读取数据
func Get(
	ctx context.Context,
	key string,
) (string, error) {
	return RedisClient.Get(ctx, key).Result()
}

//Hash 相关操作

//HSet 操作Hash类型, 将数据写入Hash结构中
//	key	    是Redis查找Hash结构的key
//	id-data 就是Hash结构内k-v
func HSet(
	ctx context.Context,
	key string,
	id string,
	data interface{},
) error {
	value, err := json.Marshal(data)
	if err != nil {
		return err
	}
	return RedisClient.HSet(ctx, key, id, value).Err()
}

//HGet 操作Hash类型, 从Hash结构中读取数据
//	key 是Redis查找Hash结构的key
//	k   是Hash结构内k-v中的k
func HGet(
	ctx context.Context,
	key string,
	k string,
) (string, error) {
	return RedisClient.HGet(ctx, key, k).Result()
}

func HGetAll(
	ctx context.Context,
	key string,
) (map[string]string, error) {
	return RedisClient.HGetAll(ctx, key).Result()
}

//HMGet 操作Hash类型, 从Hash结构中批量读取数据
//	key 是Redis查找Hash结构的key
//	k...不定参数 类型string
func HMGet(
	ctx context.Context,
	key string,
	k ...string,
) ([]interface{}, error) {
	return RedisClient.HMGet(ctx, key, k...).Result()
}

//HExist 操作Hash类型, 判断key存在于Hash结构中
func HExist(
	ctx context.Context,
	key string,
	k string,
) bool {
	exist, err := RedisClient.HExists(ctx, key, k).Result()
	if err != nil {
		return false
	}
	return exist
}

//HDelete 操作Hash类型, 从Hash结构中删除数据
//	key 是Redis查找Hash结构的key
//	k   是Hash结构内k-v中的k
func HDelete(
	ctx context.Context,
	key string,
	k string,
) (bool, error) {
	value, err := RedisClient.HDel(ctx, key, k).Result()
	return (value > 0), err
}

//List 相关操作

//LPush 操作List类型, 将新元素从表头推入List
func LPush(
	ctx context.Context,
	key string,
	value interface{},
) error {
	return RedisClient.LPush(ctx, key, value).Err()
}

//RPush 操作List类型, 将新元素从表尾推入List
func RPush(
	ctx context.Context,
	key string,
	value interface{},
) error {
	return RedisClient.RPush(ctx, key, value).Err()
}

//LLen 操作List类型, 返回List内元素个数
func LLen(
	ctx context.Context,
	key string,
) (int64, error) {
	return RedisClient.LLen(ctx, key).Result()
}

//LRange 操作List类型, 返回指定范围内元素
func LRange(
	ctx context.Context,
	key string,
	start int64,
	stop int64,
) ([]string, error) {
	return RedisClient.LRange(ctx, key, start, stop).Result()
}

//LRemove 操作List类型, 从表尾向表头搜索移除1个Value
func LRemove(
	ctx context.Context,
	key string,
	value interface{},
) error {
	return RedisClient.LRem(ctx, key, -1, value).Err()
}

//LTrim 操作List类型，保留指定区间内的元素
func LTrim(
	ctx context.Context,
	key string,
	start, stop int64,
) error {
	return RedisClient.LTrim(ctx, key, start, stop).Err()
}

//LRPop 操作List类型, 从表尾移除一个元素
func RPop(
	ctx context.Context,
	key string,
) error {
	return RedisClient.RPop(ctx, key).Err()
}

//LRPop 操作List类型, 从表头移除一个元素
func LPop(
	ctx context.Context,
	key string,
) error {
	return RedisClient.LPop(ctx, key).Err()
}

//Set 相关操作

//SAdd 操作Set类型, 将数据member写入Set结构中
func SAdd(
	ctx context.Context,
	key string,
	member string,
) error {
	return RedisClient.SAdd(ctx, key, member).Err()
}

//SRemove 操作Set类型, 将数据member从Set结构中移除
func SRemove(
	ctx context.Context,
	key string,
	member string,
) error {
	return RedisClient.SRem(ctx, key, member).Err()
}

//SIsMember 操作Set类型, 判断member在Set结构中
func SIsMember(
	ctx context.Context,
	key string,
	member string,
) bool {
	isMember, err := RedisClient.SIsMember(ctx, key, member).Result()
	if err != nil {
		return false
	}
	return isMember
}

//ZSet 相关操作

//ZAdd 操作ZSet类型, 将Member写入ZSet结构中
func ZAdd(
	ctx context.Context,
	key string,
	member interface{},
	score float64,
) error {
	z := redis.Z{
		Score:  score,
		Member: member,
	}
	return RedisClient.ZAdd(ctx, key, &z).Err()
}

//ZCount 操作ZSet类型, 为有序集key中的成员member的score值加上增量increment
func ZIncrBy(
	ctx context.Context,
	key string,
	increment float64,
	member interface{},
) (float64, error) {
	str := fmt.Sprintf("%v", member)
	return RedisClient.ZIncrBy(ctx, key, increment, str).Result()
}

//ZScore 操作ZSet类型, 获取有序集key中的成员member的score
func ZScore(
	ctx context.Context,
	key string, member interface{}) (float64, error) {
	str := fmt.Sprintf("%v", member)
	return RedisClient.ZScore(ctx, key, str).Result()
}

//ZCount 操作ZSet类型, 获取有序集合的成员数
func ZCard(
	ctx context.Context,
	key string,
) (int64, error) {
	return RedisClient.ZCard(ctx, key).Result()
}

//ZCount 操作ZSet类型, 统计分值在给定范围内节点数量
func ZCount(
	ctx context.Context,
	key string,
	min string,
	max string,
) (int64, error) {
	return RedisClient.ZCount(ctx, key, min, max).Result()
}

//ZRange 操作ZSet类型, (从小到大)返回给定范围内所有元素
func ZRange(
	ctx context.Context,
	key string,
	start int64,
	stop int64,
) ([]string, error) {
	return RedisClient.ZRange(ctx, key, start, stop).Result()
}

//ZRevRange 操作ZSet类型, (从大到小)返回给定范围内所有元素
func ZRevRange(
	ctx context.Context,
	key string,
	start int64,
	stop int64,
) ([]string, error) {
	return RedisClient.ZRevRange(ctx, key, start, stop).Result()
}

//ZRemove 操作ZSet类型, 删除包含给定成员的所有节点和分值节点
func ZRemove(
	ctx context.Context,
	key string,
	member interface{},
) error {
	return RedisClient.ZRem(ctx, key, member).Err()
}

//ZRemove 操作ZSet类型, 删除包含给定成员的所有节点和分值节点
func ZRemRangeByRank(
	ctx context.Context,
	key string,
	start, stop int64,
) error {
	return RedisClient.ZRemRangeByRank(ctx, key, start, stop).Err()
}
